package edu.xidian.cnmano.service.impl;

import edu.xidian.cnmano.dao.K8svaluesMapper;
import edu.xidian.cnmano.dao.NsinstanceMapper;
import edu.xidian.cnmano.dao.VnfinstanceMapper;
import edu.xidian.cnmano.entities.monitor.NodeInfo;
import edu.xidian.cnmano.entities.nsdmanagement.Oscontainerdesc;
import edu.xidian.cnmano.entities.nsdmanagement.Vduprofile;
import edu.xidian.cnmano.entities.nsdmanagement.Virtualizednetworkfunctiondescriptor;
import edu.xidian.cnmano.entities.nsorchestrate.K8svalues;
import edu.xidian.cnmano.entities.nsorchestrate.K8svaluesExample;
import edu.xidian.cnmano.entities.nsorchestrate.Vnfinstance;
import edu.xidian.cnmano.entities.nsorchestrate.VnfinstanceExample;
import edu.xidian.cnmano.entities.nstransform.VirtualizednetworkfunctiondeploymentflavourFull;
import edu.xidian.cnmano.entities.nstransform.VirtualizednetworkfunctiondescriptorFull;
import edu.xidian.cnmano.entities.nstransform.VnfInstanceFull;
import edu.xidian.cnmano.entities.templatemanagement.Template;
import edu.xidian.cnmano.requestbody.ScaleVnfData;
import edu.xidian.cnmano.service.K8sValuesService;
import edu.xidian.cnmano.service.VnfinstanceService;
import edu.xidian.cnmano.utils.RestTemplateUtil;
import edu.xidian.cnmano.utils.SystemCallUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Random;

/**
 * @author zhr
 * @date 2021/1/15-15:01
 */
@Service
@Slf4j
public class VnfinstanceServiceImpl implements VnfinstanceService {

    @Resource
    private VnfinstanceMapper vnfinstanceMapper;

    @Resource
    private NsinstanceMapper nsinstanceMapper;

    @Resource
    private K8svaluesMapper valuesMapper;

    @Resource
    private K8sValuesService k8sValuesService;

    @Override
    public List<Vnfinstance> listVnfinstance() {
        VnfinstanceExample example = new VnfinstanceExample();
        return vnfinstanceMapper.selectByExample(example);
    }

    /**
     * 前端发来的vnfinstance应该是不完整的，id由数据库自动生成
     * vnfInstanceName，vnfInstanceDescription，vnfdId，flavourId由前端数据提供
     * vnfProvider，vnfProductName，vnfSoftwareVersion，vnfdVersion从NsdManagement处获取
     * instantiationState默认为NOT_INSTANTIATED
     * vnfState（也就是pod的state）需要从monitor服务处获取
     *
     * @param vnfinstance
     * @return
     * @date 2020.01.15
     */
    @Override
    public int createVnfinstance(Vnfinstance vnfinstance) {
        List<Virtualizednetworkfunctiondescriptor> vnfds = RestTemplateUtil.doListVnfd();
        for (Virtualizednetworkfunctiondescriptor vnfd : vnfds) {
            if (vnfd.getVnfdId() == vnfinstance.getVnfdId()) {
                vnfinstance.setVnfProvider(vnfd.getVnfProvider());
                vnfinstance.setVnfProductName(vnfd.getVnfProductName());
                vnfinstance.setVnfSoftwareVersion(vnfd.getVnfSoftwareVersion());
                vnfinstance.setVnfdVersion(vnfd.getVnfdVersion());
            }
        }
        vnfinstance.setInstantiationState("NOT_INSTANTIATED");
        //因为监控服务还没有开发完全，目前还是只设置为CREATED状态
        vnfinstance.setVnfState("CREATED");
        //这里设置要返回新插入的id值，使得创建ns实例的时候可以直接统计出来vnfInstance的id
        vnfinstanceMapper.insert(vnfinstance);


        String vnfdName = vnfinstance.getVnfProductName();
        String vnfdVersion = vnfinstance.getVnfdVersion();

        String currentDir = System.getProperty("user.dir")+"/..";
        String command = "bash "+currentDir+"/scripts/createVnfInstance.sh " + vnfdName + "-" + vnfdVersion;
        SystemCallUtil.exec(command);

        return vnfinstance.getId();
    }

    @Override
    public int instantiateVnfinstance(int id, String nsInstanceName) {
        Vnfinstance vnfinstance = vnfinstanceMapper.selectByPrimaryKey(id);
        //已经实例化的就不需要再次实例化了
        if (vnfinstance.getInstantiationState().equals("INSTANTIATED")) {
            return 1;
        }
        //先把基础内容拷贝上
        VnfInstanceFull vnfInstanceFull = new VnfInstanceFull();
        vnfInstanceFull.setId(vnfinstance.getId());
        vnfInstanceFull.setVnfInstanceName(vnfinstance.getVnfInstanceName());
        vnfInstanceFull.setVnfInstanceDescription(vnfinstance.getVnfInstanceDescription());
        vnfInstanceFull.setVnfProvider(vnfinstance.getVnfProvider());
        vnfInstanceFull.setVnfProductName(vnfinstance.getVnfProductName());
        vnfInstanceFull.setVnfSoftwareVersion(vnfinstance.getVnfSoftwareVersion());
        vnfInstanceFull.setVnfdVersion(vnfinstance.getVnfdVersion());
        vnfInstanceFull.setInstantiationState(vnfinstance.getInstantiationState());
        vnfInstanceFull.setVnfState(vnfinstance.getVnfState());

        //调用nsdManagement两次，分别获取vnfdFull和vnfdfFull
        Integer vnfdId = vnfinstance.getVnfdId();
        Integer flavourId = vnfinstance.getFlavourId();
        VirtualizednetworkfunctiondescriptorFull vnfdFull = RestTemplateUtil.doGetVnfdFullById(vnfdId);
        VirtualizednetworkfunctiondeploymentflavourFull vnfdfFull = RestTemplateUtil.doGetVnfdfFullById(flavourId);
        vnfInstanceFull.setVnfd(vnfdFull);
        vnfInstanceFull.setFlavour(vnfdfFull);

        //把vnfInstanceFull发送给NsdTransform转换为k8s values并保存
        K8svalues k8svalues = RestTemplateUtil.doTransVnfInstanceToK8SValues(vnfInstanceFull);
        //最后吧nsInstanceName加上
        valuesMapper.insert(k8svalues);

        //将k8svalues转换为values.yaml
        k8sValuesService.k8sValuesToFile(k8svalues.getId());

        String vnfdName = vnfinstance.getVnfProductName();
        String vnfdVersion = vnfinstance.getVnfdVersion();

        String currentDir = System.getProperty("user.dir")+"/..";
        long startTime = System.currentTimeMillis();
        String command = "bash "+currentDir+"/scripts/instantiateVnfInstance.sh " + k8svalues.getId() + " " + vnfdName + "-" + vnfdVersion + " " + vnfinstance.getVnfInstanceName() + "-" + id + " " + nsInstanceName;
        SystemCallUtil.exec(command);
        long endTime = System.currentTimeMillis();
        log.info("将Vnf实例参数渲染为配置文件并提交给k8s{}，占用时间{}ms", id, endTime - startTime);


        //更新vnfInstance状态
        Vnfinstance toUpdate = new Vnfinstance();
        toUpdate.setInstantiationState("INSTANTIATED");
        toUpdate.setId(id);
        return vnfinstanceMapper.updateByPrimaryKeySelective(toUpdate);
    }

    @Override
    public int scaleVnfinstance(ScaleVnfData scaleVnfData, String nsInstanceName) {
        //通过vnfinstaceid获取vnfinstacename等参数
        Integer vnfInstanceId = scaleVnfData.getVnfInstanceId();
        Vnfinstance vnfinstanceToScale = vnfinstanceMapper.selectByPrimaryKey(vnfInstanceId);
        String vnfInstanceName = vnfinstanceToScale.getVnfInstanceName();
        Integer vnfcTargetNumber = scaleVnfData.getVnfcTargetNumber();
        String vnfdName = vnfinstanceToScale.getVnfProductName();
        String vnfdVersion = vnfinstanceToScale.getVnfdVersion();

        String currentDir = System.getProperty("user.dir")+"/..";
        //然后直接通过helm命令来控制副本数目
        long startTime = System.currentTimeMillis();
        String command = "bash "+currentDir+"/scripts/scaleVnfInstance.sh " + vnfcTargetNumber + " " + vnfInstanceName + "-" + vnfInstanceId + " " + vnfdName + "-" + vnfdVersion + " " + nsInstanceName;
        SystemCallUtil.exec(command);
        long endTime = System.currentTimeMillis();
        log.info("伸缩ns实例{}，占用时间{}ms", scaleVnfData.getVnfInstanceId(), endTime - startTime);

        return 1;
    }


    @Override
    public int terminateVnfinstance(int id, String nsInstanceName) {
        Vnfinstance vnfinstance = vnfinstanceMapper.selectByPrimaryKey(id);
        //如果ns实力已经终止了，就不需要再终止了
        if (vnfinstance.getInstantiationState().equals("NOT_INSTANTIATED")) {
            return 1;
        }

        String currentDir = System.getProperty("user.dir")+"/..";
        long startTime = System.currentTimeMillis();
        String command = "bash "+currentDir+"/scripts/terminateVnfInstance.sh " + vnfinstance.getVnfInstanceName() + "-" + id + " " + nsInstanceName;
        SystemCallUtil.exec(command);
        long endTime = System.currentTimeMillis();
        log.info("将终止请求发送给k8s{}，占用时间{}ms", id, endTime - startTime);

        //删除对应的values
        K8svaluesExample example = new K8svaluesExample();
        example.createCriteria().andVnfInstanceIdEqualTo(id);
        valuesMapper.deleteByExample(example);

        //更新vnfinstance的状态
        Vnfinstance toUpdate = new Vnfinstance();
        toUpdate.setInstantiationState("NOT_INSTANTIATED");
        toUpdate.setId(id);
        return vnfinstanceMapper.updateByPrimaryKeySelective(toUpdate);
    }


    @Override
    public int deleteVnfinstance(int id) {
        Vnfinstance vnfinstance = vnfinstanceMapper.selectByPrimaryKey(id);
        String vnfdName = vnfinstance.getVnfProductName();
        String vnfdVersion = vnfinstance.getVnfdVersion();

        String currentDir = System.getProperty("user.dir")+"/..";
        String command = "bash "+currentDir+"/scripts/deleteVnfInstance.sh " + vnfdName + "-" + vnfdVersion;
        SystemCallUtil.exec(command);
        return vnfinstanceMapper.deleteByPrimaryKey(id);
    }


    @Override
    public int scaleVnfinstanceTest(ScaleVnfData scaleVnfData) {
        //从nsd管理定位相应的资源描述文件
        Integer vnfInstanceId = scaleVnfData.getVnfInstanceId();
        Vnfinstance vnfinstance = vnfinstanceMapper.selectByPrimaryKey(vnfInstanceId);
        Integer vnfdId = vnfinstance.getVnfdId();
        Oscontainerdesc oscontainerdesc = RestTemplateUtil.doGetOscontainerdescByVnfdId(vnfdId);
        Vduprofile vduprofile = RestTemplateUtil.doGetVduProfileByVnfdId(vnfdId);
        //将扩缩容请求转换为k8s参数
        K8svalues k8svalues = RestTemplateUtil.doTransOscontainerDescToK8SValues(oscontainerdesc);

        //查询剩余资源是否可用，扩容检查，缩容不检查
        if (scaleVnfData.getScaleVnfType().equals("SCALE_OUT")) {
            List<NodeInfo> nodeInfoList = RestTemplateUtil.doListNodeInfo();
            Integer totalCpuLeft = 0;
            Integer totalMemoryLeft = 0;
            for (NodeInfo nodeInfo : nodeInfoList) {
                totalCpuLeft += nodeInfo.getTotalCpu() - nodeInfo.getUsedCpu();
                totalMemoryLeft += nodeInfo.getTotalMemory() - nodeInfo.getUsedMemory();
            }
            Integer gap = scaleVnfData.getVnfcTargetNumber()-vduprofile.getMinNumberOfInstances();
            Integer requestedCpu = oscontainerdesc.getRequestedCpuResources();
            Integer requestedMemory = oscontainerdesc.getRequestedMemoryResources();
            if(gap*requestedMemory>totalMemoryLeft) {
                //内存不够，不给扩容
                return 0;
            }
        }

        //获取所需模板(假装)
        List<Template> templates = RestTemplateUtil.doListTemplate();

        //渲染，形成配置文件，在服务器中测试，渲染需要800毫秒左右，20毫秒上下浮动
        try {
            Random random = new Random();
            int floatTime = random.nextInt(41) - 20;
            Thread.sleep(800+floatTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //提交配置文件，在服务器中测试，渲染需要865毫秒左右，30毫秒上下浮动
        try {
            Random random = new Random();
            int floatTime = random.nextInt(61) - 30;
            Thread.sleep(865+floatTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1;
    }

}
